package top.yongyan0624.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import top.yongyan0624.Utils.RedisUtil;
import top.yongyan0624.common.lang.Const;
import top.yongyan0624.entity.SysMenu;
import top.yongyan0624.entity.SysRole;
import top.yongyan0624.entity.SysUser;
import top.yongyan0624.entity.SysUserRole;
import top.yongyan0624.mapper.SysUserMapper;
import top.yongyan0624.service.SysMenuService;
import top.yongyan0624.service.SysRoleService;
import top.yongyan0624.service.SysUserRoleService;
import top.yongyan0624.service.SysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 讽惘
 * @since 2021-05-13
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysMenuService sysMenuService;

    @Autowired
    protected SysUserRoleService sysUserRoleService;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    protected BCryptPasswordEncoder encoder;

    @Override
    public Page<SysUser> queryList(Page<SysUser> page, String username) {
        Page<SysUser> userPage = sysUserMapper.selectPage(page, new QueryWrapper<SysUser>()
                .like(StringUtils.isNotBlank(username), "username", username)
                .orderByDesc("last_login"));

        //对内部roles角色进行查询
        userPage.getRecords().forEach(this::setRoles);
        return userPage;
    }

    @Override
    public Page<SysUser> queryUserByRoleName(Page<SysUser> page, String roleName) {
        Page<SysUser> userPage = sysUserMapper.selectPage(page,
                new QueryWrapper<SysUser>().inSql("id", "select sur.user_id " +
                        "from sys_user_role sur " +
                        "where sur.role_id in (select sr.id from sys_role sr where name = '" + roleName + "')")
                        .orderByAsc("username"));
        userPage.getRecords().forEach(this::setRoles);
        return userPage;
    }

    @Override
    public void setRoles(SysUser user) {
        List<Long> roleIds = sysUserRoleService.list(new QueryWrapper<SysUserRole>()
                .eq("user_id", user.getId()))
                .stream()
                .map(SysUserRole::getRoleId)
                .collect(Collectors.toList());
        if (roleIds.size() > 0) {
            user.getRoles().addAll(sysRoleService.list(new QueryWrapper<SysRole>()
                    .in("id", roleIds)
                    .orderByAsc("priority")
            ));
        }
    }

    @Override
    public SysUser register(String username, String password) {
        SysUser user = new SysUser();
        user.setUsername(username);
        user.setPassword(encoder.encode(password));
        user.setAvatar(Const.DEFULT_AVATAR);
        sysUserMapper.insert(user);
        //角色的初始化
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setUserId(user.getId());
        sysUserRole.setRoleId(3L); //注册的用户默认为游客,roleId:3

        if (sysUserRoleService.save(sysUserRole)) {
            return user;
        }
        return null;
    }

    @Transactional
    @Override
    public boolean addUser(SysUser sysUser) {
        if (sysUser.getPassword() == null) {
            sysUser.setPassword(Const.DEFULT_PASSWORD);
        }
        sysUser.setPassword(encoder.encode(sysUser.getPassword()));
        sysUser.setAvatar(Const.DEFULT_AVATAR);
        sysUserMapper.insert(sysUser);
        //角色初始化
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setUserId(sysUser.getId());
        sysUserRole.setRoleId(3L);

        return sysUserRoleService.save(sysUserRole);
    }

    @Transactional
    @Override
    public boolean rolePerm(Long userId, Long[] roleIds) {
        List<SysUserRole> userRoles = new ArrayList<>();

        Arrays.stream(roleIds).forEach(r -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setRoleId(r);
            sysUserRole.setUserId(userId);

            userRoles.add(sysUserRole);
        });

        sysUserRoleService.remove(new QueryWrapper<SysUserRole>().eq("user_id", userId));
        sysUserRoleService.saveBatch(userRoles);

        // 删除缓存
        this.clearUserAuthorityInfo(userId);

        return true;
    }

    @Override
    public SysUser getByUsername(String username) {
        return getOne(new QueryWrapper<SysUser>().eq("username", username));
    }

    @Override
    public String getUserAuthorityInfo(Long userId) {
        String authority = "";

        //这里做一下权限信息的缓存
        if (redisUtil.hasKey("grantedAuthority:" + userId)) {
            //说明已经存在权限信息的缓存，直接赋值即可
            authority = (String) redisUtil.get("grantedAuthority:" + userId);
        } else {
            //没有查询到缓存，开始获取用户的权限信息的缓存

            //获取角色编码(ROLE_admin,ROLE_normal,)
            List<SysRole> roles = sysRoleService.list(new QueryWrapper<SysRole>()
                    .inSql("id", "select role_id from sys_user_role where user_id = " + userId));

            if (roles.size() > 0) {
                //通过流 映射roles中的所有role中的Code，加上前缀后 用 , 号缝合起来
                String roleCodes = roles.stream().map(role -> "ROLE_" + role.getCode()).collect(Collectors.joining(","));

                authority = roleCodes.concat(",");
            }

            //获取菜单操作编码(sys:user:list...)
            //比较麻烦的是需要通过userId获取roleId，再通过roleId获取对应的menuId，通过menuId最后获取菜单操作编码（perms）
            List<Long> menuIds = sysUserMapper.getNavMenuIds(userId);

            if (menuIds.size() > 0) {

                List<SysMenu> menus = sysMenuService.listByIds(menuIds);
                String menuPerms = menus.stream().map(menu -> menu.getPerms()).collect(Collectors.joining(","));

                authority = authority.concat(menuPerms);
            }

            //将信息缓存到redis，前缀:userId 作为key，value就是权限字符串，缓存时间为一个小时
            redisUtil.set("grantedAuthority:" + userId, authority, 60 * 60);
        }

        return authority;
    }

    @Override
    public void clearUserAuthorityInfo(Long userId) {
        redisUtil.del("grantedAuthority:" + userId);
    }

    @Override
    public void clearUserAuthorityInfoByRoleId(Long roleId) {
        //查出所有具有这个role的用户
        List<SysUser> sysUsers = this.list(new QueryWrapper<SysUser>()
                .inSql("id", "select user_id from sys_user_role where role_id = " + roleId));
        //使所有用户执行清除权限缓存的方法
        sysUsers.forEach(u -> {
            this.clearUserAuthorityInfo(u.getId());
        });
    }

    @Override
    public void clearUserAuthorityInfoByMenuId(Long menuId) {
        //查出所有具有这个menu操作权限的用户
        //通过MenuId查role，再通过role查user
        List<SysUser> sysUsers = sysUserMapper.listByMenuId(menuId);
        //使所有用户执行清除权限缓存的方法
        sysUsers.forEach(u -> {
            this.clearUserAuthorityInfo(u.getId());
        });
    }
}
